#include <string.h>
#include "gkt_debug.h"
#include "gkt_malloc.h"
#include "gkt_window.h"
#include "widget_draw.h"

#define WIDGET_INPUTBOX_NUMBER_KEY_MASK_BITS	\
	(1U << GKT_KEY_0) | (1U << GKT_KEY_1) | (1U << GKT_KEY_2) \
		| (1U << GKT_KEY_3) | (1U << GKT_KEY_4) | (1U << GKT_KEY_5) \
		| (1U << GKT_KEY_6) | (1U << GKT_KEY_7) | (1U << GKT_KEY_8) \
		| (1U << GKT_KEY_9)


/************************************************************
 *
 * X_DIST
 * |<-->|
 * ----------------------------------
 * |    0    1    2    3    4    5  |
 * ----------------------------------
 *      |<-->|
 *      X_SPACE
 *
 ************************************************************/
static void widget_inputbox_key_voice_mask(gkt_widget_s *widget,
				uint32_t mask)
{
	if (!(widget->flags & GKT_WIDGET_F_KEY_VOICE_BYPASS))
		gkt_key_voice_mask(mask);
}

#if defined(GKT_CONFIG_SCREEN_ENABLE) && GKT_CONFIG_SCREEN_ENABLE

typedef struct _widget_inputbox_private_data {
	int	password;

	uint32_t	data_maxsize;
	uint32_t	accept_minsize;	/* accept_minsize <= accept_maxsize */
	uint32_t	accept_maxsize;	/* accept_maxsize <= data_maxsize */

	int	asterisk_timer;	/* is applicable to PASSWORD inputbox ONLY */
	uint32_t	font_width;
	uint32_t	display_maxsize;	/* display_maxsize <= accept_maxsize */
	uint32_t	display_size;	/* include cursor if displayed */
	uint32_t	display_start;

	gkt_widget_inputbox_data_s	data;
} widget_inputbox_private_data_s;

#ifdef GKT_CONFIG_INPUTBOX_CURSOR_CHARACTER
#define INPUTBOX_CURSOR_CHARACTER	GKT_CONFIG_INPUTBOX_CURSOR_CHARACTER
#else
#define INPUTBOX_CURSOR_CHARACTER	'-'
#endif

/* password display */
#define WIDGET_INPUTBOX_PASSWORD_DISPLAY_PLAINTEXT_ASTERISK	1
#define WIDGET_INPUTBOX_PASSWORD_DISPLAY_ASTERISK	2

#define WIDGET_INPUTBOX_PASSWORD_GEN_DISPLAY_MODE_L1(name)	\
	WIDGET_INPUTBOX_PASSWORD_DISPLAY_##name
#define WIDGET_INPUTBOX_PASSWORD_GEN_DISPLAY_MODE(name)	\
	WIDGET_INPUTBOX_PASSWORD_GEN_DISPLAY_MODE_L1(name)

#ifdef GKT_CONFIG_WIDGET_INPUTBOX_PASSWORD_DISPLAY_MODE
#define WIDGET_INPUTBOX_PASSWORD_DISPLAY_MODE	\
	WIDGET_INPUTBOX_PASSWORD_GEN_DISPLAY_MODE(GKT_CONFIG_WIDGET_INPUTBOX_PASSWORD_DISPLAY_MODE)
#if !WIDGET_INPUTBOX_PASSWORD_DISPLAY_MODE
#error 'GKT_CONFIG_WIDGET_INPUTBOX_PASSWORD_DISPLAY_MODE -- unsupport.'
#endif
#else
#define WIDGET_INPUTBOX_PASSWORD_DISPLAY_MODE	\
	WIDGET_INPUTBOX_PASSWORD_DISPLAY_PLAINTEXT_ASTERISK
#endif
#define WIDGET_INPUTBOX_PASSWORD_DISPLAY_PLAINTEXT_TO_ASTERISK_TIME	500

enum {
	WIDGET_INPUTBOX_TIMER_ASTERISK = 0,
	WIDGET_INPUTBOX_TIMER_MAX
};

static const char sc_widget_inputbox_cursor_text[1] = 
	{ INPUTBOX_CURSOR_CHARACTER };
static const char sc_widget_inputbox_asterisk_text[1] = { '*' };

static void widget_inputbox_init_rect(
				gkt_widget_s *widget, gkt_rect_s *rect, 
				uint32_t offset, uint32_t nums)
{
	widget_inputbox_private_data_s *private_data;

	private_data = (widget_inputbox_private_data_s *)widget->private_data;

	rect->x = GKT_WIDGET_X(widget) + GKT_WIDGET_BORDER_WIDTH + GKT_WIDGET_TEXT_X_DIST 
				+ (uint16_t)((private_data->font_width + GKT_WIDGET_TEXT_X_SPACE) * offset);
	rect->y = GKT_WIDGET_Y(widget) + GKT_WIDGET_BORDER_WIDTH;
	rect->width = (uint16_t)(private_data->font_width + GKT_WIDGET_TEXT_X_SPACE) * nums;
	rect->height = GKT_WIDGET_HEIGHT(widget) - (GKT_WIDGET_BORDER_WIDTH * 2);
}

static void widget_inputbox_clear_text_region(
					gkt_widget_s *widget,
					uint32_t offset, uint32_t nums, 
					uint32_t focus, uint32_t update)
{
	gkt_rect_s rect;

	widget_inputbox_init_rect(widget, &rect, offset, nums);
	widget_draw_background(widget, &rect, focus, update);
}

static void widget_inputbox_draw_text(
				gkt_widget_s *widget, const char *text, 
				uint32_t offset, uint32_t nums, 
				uint32_t focus, uint32_t update)
{
	gkt_rect_s rect;

	if (nums > 0) {
		widget_inputbox_init_rect(widget, &rect, offset, nums);
		widget_draw_text(widget, &rect, text, nums, focus, update);
	}
}

static void widget_inputbox_reset(gkt_widget_s *widget, 
			widget_inputbox_private_data_s *private_data)
{
	if (private_data->asterisk_timer) {
		gkt_window_timer_clear(GKT_WINDOW_TIMER_WIDGET | widget->id);
		private_data->asterisk_timer = 0;
	}

	if (widget->flags & GKT_WIDGET_F_SHOW) {
		widget_inputbox_clear_text_region(widget, 0, 
				private_data->display_maxsize, 0, 1);

		if (widget->flags & GKT_WIDGET_F_FOCUS) {
			widget_inputbox_draw_text(widget, 
				sc_widget_inputbox_cursor_text, 
				0, 1, 0, 1);
		}
	}

	if (private_data->data.size == private_data->accept_maxsize)
		widget_inputbox_key_voice_mask(widget, 0);

	private_data->display_size = 0;
	private_data->display_start = 0;
	private_data->data.size = 0;
	private_data->data.focus_index = 0;
}

static int widget_inputbox_init(gkt_widget_s *widget,
				gkt_widget_inputbox_attr_u *attr)
{
	widget_inputbox_private_data_s *private_data;
	uint32_t size, text_region_width;

	if (!attr->min_size || !attr->max_size 
		|| (attr->max_size < attr->min_size))
	{
		return GKT_EPARAM;
	}

	private_data = (widget_inputbox_private_data_s *)widget->private_data;
	if (!private_data 
		|| (private_data->data_maxsize < (uint32_t)attr->max_size)) 
	{
		size = sizeof(widget_inputbox_private_data_s);
		if (attr->max_size > GKT_WIDGET_INPUTBOX_DATA_DEFAULT_SIZE)
			size += attr->max_size - GKT_WIDGET_INPUTBOX_DATA_DEFAULT_SIZE;
		private_data = (widget_inputbox_private_data_s *)gkt_malloc(size);
		if (__UNLIKELY(!private_data))
			return GKT_ENOMEMORY;
		else if (widget->private_data)
			gkt_free((void *)widget->private_data);

		memset(private_data, 0, size);

		private_data->font_width = gkt_font_get_width(GKT_FONT_CODE_ASCII, 
										widget->desc->font_size);
		private_data->data_maxsize = attr->max_size;


		/* text_region_width: subtract the border width and x_dist  */
		text_region_width = GKT_WIDGET_WIDTH(widget) - (GKT_WIDGET_BORDER_WIDTH * 2) 
								- (GKT_WIDGET_TEXT_X_DIST * 2);
		private_data->display_maxsize = text_region_width / 
			(private_data->font_width + GKT_WIDGET_TEXT_X_SPACE);
#if (GKT_WIDGET_TEXT_X_SPACE > 0)
		{
			uint32_t text_region_width_1;

			text_region_width_1 = private_data->display_maxsize * 
									(private_data->font_width + GKT_WIDGET_TEXT_X_SPACE)
									+ private_data->font_width;
			if (text_region_width_1 <= text_region_width)
				private_data->display_maxsize++;
		}
#endif

		widget->private_data = (uint32_t)private_data;
	}

	private_data->password = attr->password ? 1 : 0;
	private_data->accept_minsize = attr->min_size;
	private_data->accept_maxsize = attr->max_size;

	widget_inputbox_reset(widget, private_data);

	return GKT_EVENT_HANDLE_COMPLETE;
}

static uint32_t widget_inputbox_display_start(
					widget_inputbox_private_data_s *private_data)
{
	uint32_t display_start, display_size;

	if (private_data->display_size < private_data->data.size) {
		display_size = private_data->data.size - private_data->data.focus_index;
		if (private_data->display_size <= display_size)
			display_start = private_data->data.focus_index;
		else
			display_start = private_data->data.size - private_data->display_size;
	}
	else
		display_start = 0;

	return display_start;
}

static void widget_inputbox_paint_focus(gkt_widget_s *widget, 
				widget_inputbox_private_data_s *private_data,
				uint32_t update)
{
	uint32_t i, focus;

	for (i = 0; i < private_data->display_size; i++) {
		focus = ((private_data->display_start + i) == private_data->data.focus_index) ? 1 : 0;
		widget_inputbox_draw_text(widget, 
				&private_data->data.data[private_data->display_start + i],
				i, 1, focus, update);
	}
}

static void widget_inputbox_paint(gkt_widget_s *widget, 
				widget_inputbox_private_data_s *private_data,
				uint32_t update)
{
	uint32_t i;

	if (__UNLIKELY(!private_data->display_maxsize))
		return;

	private_data->display_size = gkt_min(private_data->data.size, 
									private_data->display_maxsize);
	if ((widget->flags & GKT_WIDGET_F_FOCUS)
		&& (private_data->display_size == private_data->display_maxsize) 
		&& (private_data->data.focus_index == private_data->data.size)) 
	{
		if (private_data->data.size < private_data->accept_maxsize)
			private_data->display_size--;
		else
			private_data->data.focus_index--;
	}

	gkt_trace("inputbox_paint: size(%u-%u)(%u-%u)\n", 
				private_data->data.size, private_data->accept_maxsize,
				private_data->display_size, private_data->display_maxsize);

	widget_draw_background(widget, NULL, 0, 0);
	widget_draw_border(widget, 0);

	if (private_data->display_size) {
		if (private_data->password) {
			for (i = 0; i < private_data->display_size; i++) {
				widget_inputbox_draw_text(widget, 
						sc_widget_inputbox_asterisk_text, 
						i, 1, 0, 0);
			}
		}
		else if (private_data->data.focus_index == private_data->data.size) {
			private_data->display_start = private_data->data.size - private_data->display_size;
			widget_inputbox_draw_text(widget, 
					&private_data->data.data[private_data->display_start], 
					0, private_data->display_size, 0, 0);
		}
		else {
			private_data->display_start = widget_inputbox_display_start(private_data);
			if (widget->flags & GKT_WIDGET_F_FOCUS)
				widget_inputbox_paint_focus(widget, private_data, 0);
			else {
				widget_inputbox_draw_text(widget, 
						&private_data->data.data[private_data->display_start],
						0, private_data->display_size, 0, 0);
			}

			goto paint_update;
		}
	}

	if ((widget->flags & GKT_WIDGET_F_FOCUS)
		&& (private_data->display_size < private_data->display_maxsize)
		&& (private_data->data.size < private_data->accept_maxsize)) 
	{
		widget_inputbox_draw_text(widget, 
				sc_widget_inputbox_cursor_text, 
				private_data->display_size, 1, 0, 0);
	}

paint_update:
	if (update)
		widget_draw_update(widget);
}


static void widget_inputbox_password_update_p2a(
				gkt_widget_s *widget, 
				widget_inputbox_private_data_s *private_data)
{
	const char *text;
	uint32_t offset;

	if ((private_data->display_size < private_data->display_maxsize)
		|| (private_data->data.size == private_data->accept_maxsize)) 
	{
		text = sc_widget_inputbox_asterisk_text;
		offset = private_data->display_size - 1;
	}
	else {
		text = sc_widget_inputbox_cursor_text;
		offset = --private_data->display_size;
	}

	widget_inputbox_draw_text(widget, text, offset, 1, 0, 1);
}

static void widget_inputbox_password_update_add(
				gkt_widget_s *widget, 
				widget_inputbox_private_data_s *private_data)
{
	int retval;

	if (private_data->asterisk_timer)
		gkt_window_timer_clear(GKT_WINDOW_TIMER_WIDGET | widget->id);

	if (private_data->display_size < private_data->display_maxsize) {
		if (private_data->asterisk_timer 
			&& (private_data->display_size > 0)) 
		{
			widget_inputbox_draw_text(widget, 
					sc_widget_inputbox_asterisk_text, 
					private_data->display_size - 1, 1, 0, 1);
		}

		/* display new digital number */
		widget_inputbox_draw_text(widget, 
				&private_data->data.data[private_data->data.size - 1], 
				private_data->display_size, 1, 0, 1);
		private_data->display_size++;

		if ((private_data->display_size < private_data->display_maxsize)
			&& (private_data->data.size < private_data->accept_maxsize)) 
		{
			/* 
			 * 1. have enough space to display the cursor character
			 * 2. not reach the max accept size 
			 */
			widget_inputbox_draw_text(widget, 
					sc_widget_inputbox_cursor_text,
					private_data->display_size, 1, 0, 1);
		}
	}
	else {
		/* display the new digital number at the last display position */
		widget_inputbox_draw_text(widget, 
				&private_data->data.data[private_data->data.size - 1], 
				private_data->display_size - 1, 1, 0, 1);
	}

	/* set timer to change new character from plaintext to asterisk('*') */
	retval = gkt_window_timer_set(GKT_WINDOW_TIMER_WIDGET | widget->id,
					WIDGET_INPUTBOX_PASSWORD_DISPLAY_PLAINTEXT_TO_ASTERISK_TIME, 
					0, private_data->data.size);
	if (__LIKELY(GKT_SUCCESS == retval))
		private_data->asterisk_timer = 1;
	else {
		private_data->asterisk_timer = 0;
		widget_inputbox_password_update_p2a(widget, private_data);
	}
}

static void widget_inputbox_password_update_delete(
				gkt_widget_s *widget, 
				widget_inputbox_private_data_s *private_data)
{
	if (private_data->asterisk_timer) {
		gkt_window_timer_clear(GKT_WINDOW_TIMER_WIDGET | widget->id);
		private_data->asterisk_timer = 0;
	}
	
	if (private_data->display_size == private_data->display_maxsize) {
		/* change the last displayed digital number or asterisk('*') to cursor character */
		private_data->display_size--;
		widget_inputbox_draw_text(widget, 
				sc_widget_inputbox_cursor_text,
				private_data->display_size, 1, 0, 1);
	}
	else if (private_data->data.size < private_data->display_size) {
		/* clear the cursor character */
		widget_inputbox_clear_text_region(widget, 
				private_data->display_size, 1, 0, 1);

		/* change the last displayed digital number or asterisk('*') to cursor character */
		private_data->display_size--;
		widget_inputbox_draw_text(widget, 
				sc_widget_inputbox_cursor_text,
				private_data->display_size, 1, 0, 1);
	}
}

static void widget_inputbox_normal_update_add(
				gkt_widget_s *widget, 
				widget_inputbox_private_data_s *private_data)
{
	if (__UNLIKELY(!private_data->display_maxsize))
		return;

	if (private_data->display_size < (private_data->display_maxsize - 1)) {
		widget_inputbox_draw_text(widget, 
				&private_data->data.data[private_data->data.size - 1], 
				private_data->display_size, 1, 0, 1);
		private_data->display_size++;

		if (private_data->data.size < private_data->accept_maxsize) {
			widget_inputbox_draw_text(widget, 
					sc_widget_inputbox_cursor_text,
					private_data->display_size, 1, 0, 1);
		}
	}
	else {
		if (private_data->data.size == private_data->accept_maxsize) {
			widget_inputbox_draw_text(widget, 
					&private_data->data.data[private_data->data.size - 1], 
					private_data->display_size, 1, 0, 1);
			private_data->display_size++;
		}
		else if (private_data->display_size) {
			private_data->display_start = private_data->data.size - private_data->display_size;
			widget_inputbox_draw_text(widget, 
					&private_data->data.data[private_data->display_start], 
					0, private_data->display_size, 0, 1);
		}
	}
}

static void widget_inputbox_normal_update_delete(
				gkt_widget_s *widget, 
				widget_inputbox_private_data_s *private_data)
{
	if (__UNLIKELY(!private_data->display_size))
		return;

	if (private_data->display_size == private_data->display_maxsize) {
		/* change the last displayed digital number to cursor character */
		private_data->display_size--;
		widget_inputbox_draw_text(widget, 
				sc_widget_inputbox_cursor_text,
				private_data->display_size, 1, 0, 1);
	}
	else if (private_data->data.size >= private_data->display_size) {
		private_data->display_start = private_data->data.size - private_data->display_size;
		widget_inputbox_draw_text(widget, 
				&private_data->data.data[private_data->display_start], 
				0, private_data->display_size, 0, 1);
	}
	else {
		/* clear the cursor character */
		widget_inputbox_clear_text_region(widget, 
				private_data->display_size, 1, 0, 1);

		/* change the last displayed digital number to cursor character */
		private_data->display_size--;
		widget_inputbox_draw_text(widget, 
				sc_widget_inputbox_cursor_text,
				private_data->display_size, 1, 0, 1);
	}
}

/*
 * current character losts focus
 * previous character gets focus
 */
static void widget_inputbox_normal_update_prev_focus(
				gkt_widget_s *widget, 
				widget_inputbox_private_data_s *private_data)
{
	uint32_t offset, old_focus_index, new_display_start;

	GKT_ASSERT(private_data->display_start <= private_data->data.focus_index);

	if (private_data->data.focus_index > 0) {
		offset = private_data->data.focus_index - private_data->display_start;
		old_focus_index = private_data->data.focus_index--;
		new_display_start = widget_inputbox_display_start(private_data);

		if (private_data->display_start == new_display_start) {
			widget_inputbox_draw_text(widget, 
					&private_data->data.data[old_focus_index], 
					offset, 1, 0, 1);
			widget_inputbox_draw_text(widget, 
					&private_data->data.data[private_data->data.focus_index], 
					offset - 1, 1, 1, 1);
		}
		else {
			private_data->display_start = new_display_start;
			widget_inputbox_paint_focus(widget, private_data, 1);
		}
	}
	else {
		gkt_window_custom_event_handle(widget->window,
				GKT_EVENT_WIDGET, GKT_WIDGET_EVENT_HEAD,
				(uint32_t)widget->id, (uint32_t)&private_data->data);
	}
}

/*
 * current character losts focus
 * next character gets focus
 */
static void widget_inputbox_normal_update_next_focus(
				gkt_widget_s *widget, 
				widget_inputbox_private_data_s *private_data)
{
	uint32_t offset, old_focus_index, new_display_start;

	GKT_ASSERT(private_data->display_start <= private_data->data.focus_index);

	offset = private_data->data.focus_index - private_data->display_start;
	old_focus_index = private_data->data.focus_index++;
	new_display_start = widget_inputbox_display_start(private_data);

	if (private_data->display_start == new_display_start) {
		if (private_data->data.focus_index < private_data->data.size) {
			widget_inputbox_draw_text(widget, 
					&private_data->data.data[old_focus_index], 
					offset, 1, 0, 1);

			widget_inputbox_draw_text(widget, 
					&private_data->data.data[private_data->data.focus_index], 
					offset + 1, 1, 1, 1);
		}
		else if (private_data->data.size < private_data->accept_maxsize) {
			widget_inputbox_draw_text(widget, 
					&private_data->data.data[old_focus_index], 
					offset, 1, 0, 1);

			if (private_data->display_size < private_data->display_maxsize) {
				widget_inputbox_draw_text(widget, 
						&private_data->data.data[private_data->data.focus_index], 
						offset + 1, 1, 0, 1);
			}
			else {
				private_data->display_start++;
				widget_inputbox_draw_text(widget, 
						&private_data->data.data[private_data->display_start], 
						0, private_data->display_size - 1, 0, 1);
			}

			widget_inputbox_draw_text(widget, 
					sc_widget_inputbox_cursor_text, 
					private_data->display_size, 1, 0, 1);
		}
		else {
			widget_inputbox_draw_text(widget, 
					&private_data->data.data[old_focus_index], 
					offset, 1, 1, 1);

			gkt_window_custom_event_handle(widget->window,
					GKT_EVENT_WIDGET, GKT_WIDGET_EVENT_DATA_FULL,
					(uint32_t)widget->id, (uint32_t)&private_data->data);
		}
	}
	else {
		private_data->display_start = new_display_start;
		widget_inputbox_paint_focus(widget, private_data, 1);
	}
}

int gkt_widget_inputbox_event_handler(gkt_widget_s *widget, 
					uint32_t major_type, uint32_t minor_type, 
					uint32_t data)
{
	gkt_widget_inputbox_attr_u attr;
	widget_inputbox_private_data_s *private_data;
	gkt_widget_inputbox_data_s *tb_data;
	int retval = GKT_EVENT_HANDLE_COMPLETE;

	private_data = (widget_inputbox_private_data_s *)widget->private_data;
	if (GKT_EVENT_WIDGET == major_type) {
		if (GKT_WIDGET_EVENT_INIT == minor_type) {
			attr.value = widget->desc->attr;
			retval = widget_inputbox_init(widget, &attr);
		}
		else if (GKT_WIDGET_EVENT_SET_ATTR == minor_type) {
			retval = data ? widget_inputbox_init(widget, 
						(gkt_widget_inputbox_attr_u *)data) : GKT_EPARAM;
		}
		else if (__LIKELY(private_data)) {
			if (GKT_WIDGET_EVENT_UNINIT == minor_type)
				gkt_free((void *)widget->private_data);
			else if (GKT_WIDGET_EVENT_SHOW == minor_type)
				widget_inputbox_paint(widget, private_data, data);
			else if (GKT_WIDGET_EVENT_HIDE == minor_type) {
				if (private_data->asterisk_timer) {
					gkt_window_timer_clear(GKT_WINDOW_TIMER_WIDGET | widget->id);
					private_data->asterisk_timer = 0;
				}

				if (data & GKT_WIDGET_PAINT_EXCUTE)
					widget_draw_clear(widget, data & GKT_WIDGET_PAINT_UPDATE ? 1 : 0);
			}
			else if (GKT_WIDGET_EVENT_RESET == minor_type)
					widget_inputbox_reset(widget, private_data);
			else if (GKT_WIDGET_EVENT_LOST_FOCUS == minor_type) {
				if (private_data->asterisk_timer) {
					gkt_window_timer_clear(GKT_WINDOW_TIMER_WIDGET | widget->id);
					private_data->asterisk_timer = 0;
				}
				widget_inputbox_paint(widget, private_data, data);
			}
			else if (GKT_WIDGET_EVENT_SET_FOCUS == minor_type)
				widget_inputbox_paint(widget, private_data, data);
			else if (GKT_WIDGET_EVENT_SET_DATA == minor_type) {
				tb_data = (gkt_widget_inputbox_data_s *)data;
				if (!private_data->password && tb_data 
					&& (tb_data->size >= private_data->accept_minsize)
					&& (tb_data->size <= private_data->accept_maxsize)) 
				{
					memcpy(private_data->data.data, tb_data->data, tb_data->size);
					private_data->data.size = tb_data->size;
					private_data->data.focus_index = gkt_min(tb_data->size, tb_data->focus_index);

					if (widget->flags & GKT_WIDGET_F_SHOW)
						widget_inputbox_paint(widget, private_data, 1);
				}
			}
		}
	}
	else if (GKT_EVENT_KEY == major_type) {
#if defined (GKT_CONFIG_KEYPAD_ENABLE) && GKT_CONFIG_KEYPAD_ENABLE		
		uint32_t *event_data = (uint32_t *)data;
#endif
		if ((minor_type >= GKT_KEY_0)
			&& (minor_type <= GKT_KEY_9))
		{
#if defined (GKT_CONFIG_KEYPAD_ENABLE) && GKT_CONFIG_KEYPAD_ENABLE		
			if (event_data[GKT_KEY_EVTDATA_ACTION] != GKT_KEY_ACTION_SHORT_PRESS)
				return GKT_EVENT_HANDLE_CONTINUE;
#endif				
			if (private_data->data.focus_index < private_data->data.size) {
				private_data->data.data[private_data->data.focus_index] = 
						'0' + ((minor_type) - GKT_KEY_0);
				widget_inputbox_normal_update_next_focus(widget, private_data);
			}
			else {
				GKT_ASSERT(private_data->data.focus_index == private_data->data.size);

				if (private_data->data.size < private_data->accept_maxsize) {
					private_data->data.data[private_data->data.size++] = 
							'0' + ((minor_type) - GKT_KEY_0);
					private_data->data.focus_index = private_data->data.size;

					if (private_data->data.size == private_data->accept_maxsize) {
						widget_inputbox_key_voice_mask(widget, 
								WIDGET_INPUTBOX_NUMBER_KEY_MASK_BITS);
					}

					if (private_data->password)
						widget_inputbox_password_update_add(widget, private_data);
					else
						widget_inputbox_normal_update_add(widget, private_data);

					gkt_window_custom_event_handle(widget->window,
							GKT_EVENT_WIDGET, GKT_WIDGET_EVENT_DATA_LENGTH,
							(uint32_t)widget->id, private_data->data.size);
				}
				else {
					gkt_window_custom_event_handle(widget->window,
							GKT_EVENT_WIDGET, GKT_WIDGET_EVENT_DATA_FULL,
							(uint32_t)widget->id, (uint32_t)&private_data->data);
				}
			}
		}
		else if (GKT_KEY_OK == minor_type) {
			if (private_data->data.size >= private_data->accept_minsize)
				gkt_window_custom_event_handle(widget->window, 
						GKT_EVENT_WIDGET, GKT_WIDGET_EVENT_CLICK,
						(uint32_t)widget->id, (uint32_t)&private_data->data);
			else
				gkt_window_custom_event_handle(widget->window,
						GKT_EVENT_WIDGET, GKT_WIDGET_EVENT_DATA_INSUFFICIENT,
						(uint32_t)widget->id, 0);
		}
		else if (GKT_KEY_BACKSPACE == minor_type) {
			if (private_data->data.size > 0) {
				if (private_data->password) {
					widget_inputbox_reset(widget, private_data);
					gkt_window_custom_event_handle(widget->window,
							GKT_EVENT_WIDGET, GKT_WIDGET_EVENT_DATA_LENGTH,
							(uint32_t)widget->id, private_data->data.size);
				}
				else if (private_data->data.focus_index == private_data->data.size) {
					if (private_data->data.size == private_data->accept_maxsize)
						widget_inputbox_key_voice_mask(widget, 0);
					
					private_data->data.size--;
					private_data->data.focus_index = private_data->data.size;

					if (private_data->password)
						widget_inputbox_password_update_delete(widget, private_data);
					else
						widget_inputbox_normal_update_delete(widget, private_data);

					gkt_window_custom_event_handle(widget->window,
							GKT_EVENT_WIDGET, GKT_WIDGET_EVENT_DATA_LENGTH,
							(uint32_t)widget->id, private_data->data.size);
				}
				else
					widget_inputbox_normal_update_prev_focus(widget, private_data);
			}
			else {
				retval = gkt_window_custom_event_handle(widget->window,
								GKT_EVENT_WIDGET, GKT_WIDGET_EVENT_DATA_EMPTY,
								(uint32_t)widget->id, 0);
			}
		}
		else
			retval = GKT_EVENT_HANDLE_CONTINUE;
	}
	else if (GKT_EVENT_TIMER == major_type) {
		if (private_data 
			&& private_data->asterisk_timer
			&& (data == private_data->data.size)) 
		{
			/* clear timer */
			gkt_window_timer_clear(GKT_WINDOW_TIMER_WIDGET | widget->id);
			private_data->asterisk_timer = 0;

			if (widget->flags & GKT_WIDGET_F_SHOW) 
				widget_inputbox_password_update_p2a(widget, private_data);
		}
	}
	else
		retval = GKT_EVENT_HANDLE_CONTINUE;

	return retval;
}

#else

typedef struct _widget_inputbox_private_data {
	uint32_t	data_maxsize;
	uint32_t	accept_minsize;	/* accept_minsize <= accept_maxsize */
	uint32_t	accept_maxsize;	/* accept_maxsize <= data_maxsize */

	gkt_widget_inputbox_data_s	data;
} widget_inputbox_private_data_s;

static void widget_inputbox_reset(gkt_widget_s *widget, 
			widget_inputbox_private_data_s *private_data)
{
	private_data->data.size = 0;
	widget_inputbox_key_voice_mask(widget, 0);
}

static int widget_inputbox_init(gkt_widget_s *widget,
				gkt_widget_inputbox_attr_u *attr)
{
	widget_inputbox_private_data_s *private_data;
	uint32_t size;

	if (!attr->min_size || !attr->max_size 
		|| (attr->max_size < attr->min_size))
	{
		return GKT_EPARAM;
	}

	private_data = (widget_inputbox_private_data_s *)widget->private_data;
	if (!private_data 
		|| (private_data->data_maxsize < (uint32_t)attr->max_size)) 
	{
		size = sizeof(widget_inputbox_private_data_s);
		if (attr->max_size > GKT_WIDGET_INPUTBOX_DATA_DEFAULT_SIZE)
			size += attr->max_size - GKT_WIDGET_INPUTBOX_DATA_DEFAULT_SIZE;
		private_data = (widget_inputbox_private_data_s *)gkt_malloc(size);
		if (__UNLIKELY(!private_data))
			return GKT_ENOMEMORY;
		else if (widget->private_data)
			gkt_free((void *)widget->private_data);

		memset(private_data, 0, size);
		private_data->data_maxsize = attr->max_size;
		widget->private_data = (uint32_t)private_data;
	}

	private_data->accept_minsize = attr->min_size;
	private_data->accept_maxsize = attr->max_size;

	widget_inputbox_reset(widget, private_data);

	return GKT_EVENT_HANDLE_COMPLETE;
}

int gkt_widget_inputbox_event_handler(gkt_widget_s *widget, 
					uint32_t major_type, uint32_t minor_type, 
					uint32_t data)
{
	gkt_widget_inputbox_attr_u attr;
	widget_inputbox_private_data_s *private_data;
	int retval = GKT_EVENT_HANDLE_COMPLETE;

	private_data = (widget_inputbox_private_data_s *)widget->private_data;
	if (GKT_EVENT_WIDGET == major_type) {
		if (GKT_WIDGET_EVENT_INIT == minor_type) {
			attr.value = widget->desc->attr;
			retval = widget_inputbox_init(widget, &attr);
		}
		else if (GKT_WIDGET_EVENT_SET_ATTR == minor_type) {
			retval = data ? widget_inputbox_init(widget, 
						(gkt_widget_inputbox_attr_u *)data) : GKT_EPARAM;
		}
		else if (__LIKELY(private_data)) {
			if (GKT_WIDGET_EVENT_UNINIT == minor_type)
				gkt_free((void *)widget->private_data);
			else if (GKT_WIDGET_EVENT_RESET == minor_type)
					widget_inputbox_reset(widget, private_data);
		}
	}
	else if (GKT_EVENT_KEY == major_type) {
		uint32_t *event_data = (uint32_t *)data;
		if ((minor_type >= GKT_KEY_0)
			&& (minor_type <= GKT_KEY_9))
		{
			if (event_data[GKT_KEY_EVTDATA_ACTION] != GKT_KEY_ACTION_SHORT_PRESS)
				return GKT_EVENT_HANDLE_CONTINUE;

			if (private_data->data.size < private_data->accept_maxsize) {
				private_data->data.data[private_data->data.size++] = 
					'0' + ((minor_type) - GKT_KEY_0);

				if (private_data->data.size == private_data->accept_maxsize) {
					widget_inputbox_key_voice_mask(widget, 
						WIDGET_INPUTBOX_NUMBER_KEY_MASK_BITS);
				}

				gkt_window_custom_event_handle(widget->window,
						GKT_EVENT_WIDGET, GKT_WIDGET_EVENT_DATA_LENGTH,
						(uint32_t)widget->id, private_data->data.size);
			}
			else {
				gkt_window_custom_event_handle(widget->window,
						GKT_EVENT_WIDGET, GKT_WIDGET_EVENT_DATA_FULL,
						(uint32_t)widget->id, (uint32_t)&private_data->data);
			}
		}
		else if (GKT_KEY_OK == minor_type) {
			if (private_data->data.size >= private_data->accept_minsize)
				gkt_window_custom_event_handle(widget->window, 
						GKT_EVENT_WIDGET, GKT_WIDGET_EVENT_CLICK,
						(uint32_t)widget->id, (uint32_t)&private_data->data);
			else
				gkt_window_custom_event_handle(widget->window,
						GKT_EVENT_WIDGET, GKT_WIDGET_EVENT_DATA_INSUFFICIENT,
						(uint32_t)widget->id, 0);
		}
		else if (GKT_KEY_BACKSPACE == minor_type) {
			if (private_data->data.size > 0) {
				widget_inputbox_reset(widget, private_data);
				gkt_window_custom_event_handle(widget->window,
						GKT_EVENT_WIDGET, GKT_WIDGET_EVENT_DATA_LENGTH,
						(uint32_t)widget->id, private_data->data.size);
			}
			else {
				retval = gkt_window_custom_event_handle(widget->window,
								GKT_EVENT_WIDGET, GKT_WIDGET_EVENT_DATA_EMPTY,
								(uint32_t)widget->id, 0);
			}
		}
		else
			retval = GKT_EVENT_HANDLE_CONTINUE;
	}
	else
		retval = GKT_EVENT_HANDLE_CONTINUE;

	return retval;
}

#endif

